﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "Window.h"
#include "Platform.h"

#ifdef PLATFORM_WINDOWS

namespace UX_STL
{
namespace _platform_
{
	
//--------------------------- CWindow -----------------------------//
/*
=================================================
	конструктор
=================================================
*/
	CWindow::CWindow () :
		_uPause(0), _bIsLooping(false), _bAllowPause(true),
		_uFPSCount(0), _uLastFPS(0), _uFPSTimer(100 + UXMath::Random::Int<int>() % 1000),
		_bFPSInCaption(true),	_pKeyboardInput(null),	_pCursor(null)
	{}
	
/*
=================================================
	деструктор
=================================================
*/
	CWindow::~CWindow ()
	{
		ASSERT( !_bIsLooping );
	}
	
/*
=================================================
	
=================================================
*/
	bool IsClassRegistred (const THandles &sHandles)
	{
		WNDCLASSA	tmp = {0};
		HINSTANCE	instance = sHandles.hInstance;
		bool	ret = GetClassInfo( instance, sHandles.sClassName.cstr(), &tmp ) == TRUE;
		return ret || ( sHandles.sClassName == tmp.lpszClassName and instance == tmp.hInstance );
	}

	bool CWindow::_s_RegisterClass (THandles &sHandles, const TWndClass &sWndClass)
	{
		sHandles.sClassName = uni_c_string( sWndClass.pName == null ? "DefaultWndClass" : sWndClass.pName );

		if ( sHandles.hInstance == null )
			sHandles.hInstance = GetModuleHandle( (LPCSTR) null );

		if ( IsClassRegistred( sHandles ) )
		{
			int i = 0;
			sHandles.sClassName = uni_c_string( string(sHandles.sClassName) + i );
			while ( IsClassRegistred( sHandles ) )	{ break; }
		}

		WNDCLASSEXA		windowClass = {0};
		windowClass.cbSize			= sizeof(windowClass);
		windowClass.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
		windowClass.lpfnWndProc		= &DefWindowProcA;
		windowClass.cbClsExtra		= 0;
		windowClass.cbWndExtra		= 0;
		windowClass.hInstance		= sHandles.hInstance;
		windowClass.hbrBackground	= (HBRUSH) sWndClass.eStyle;
		windowClass.hIcon			= (HICON) sWndClass.pIcon;
		windowClass.hCursor			= sWndClass.pCursor == null ?
										LoadCursor( null, IDC_ARROW ) :
										(HCURSOR)sWndClass.pCursor;
		windowClass.lpszMenuName	= null;
		windowClass.lpszClassName	= sHandles.sClassName.cstr();
		windowClass.hIconSm			= (HICON) sWndClass.pIcon;

		CHECK_ERR(
			RegisterClassEx( &windowClass ) != 0,
			"Can't register class"
		);

		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CWindow::_s_UnregisterClass (THandles &sHandles)
	{
		CHECK_ERR( UnregisterClass( sHandles.sClassName.cstr(), sHandles.hInstance ) != TRUE );
		sHandles.sClassName.Clear();
		return true;
	}

/*
=================================================
	создать окно в отдельном потоке
=================================================
*/
	bool CWindow::CreateInThread (Thread *pThread, const TDescriptor &sDesc, uni_c_string pCaption, const TWndClass &sWndClass)
	{
		CHECK_ERR( pThread != null );

		_s_RegisterClass( _sHandles, sWndClass );

		TCreateInThreadData *	p_data = new TCreateInThreadData( this, sDesc, pCaption, null );
		return pThread->Create( CWindow::_s_ThreadProc, p_data );
	}
	
/*
=================================================
	создать окно
=================================================
*/
	bool CWindow::Create (const TDescriptor &sDesc, uni_c_string pCaption, const TWndClass &sWndClass)
	{
		_s_RegisterClass( _sHandles, sWndClass );
		return _Create( sDesc, pCaption );
	}
		
	bool CWindow::_Create (const TDescriptor &sDesc, uni_c_string pCaption)
	{
		CHECK_ERR( IsClassRegistred( _sHandles ) );

		_sDescriptor = TDescriptor();
		
		ASSERT( All( sDesc.vSize <= WinApiPlatform::ScreenResolution() ) );

		UXMath::ivec2	v_wnd_pos	= sDesc.vPos;
		UXMath::uvec2	v_wnd_size	= sDesc.vSize;

		DWORD	wndStyle	= 0;
		DWORD	wndExtStyle	= WS_EX_APPWINDOW;
		

		// try to set full screen mode //
		if ( sDesc.bFullScreen )
		{
			if ( !WinApiPlatform::ChangeScreenResolution( sDesc.vSize, 8, sDesc.uFreq ) )
			{
				_sDescriptor.bFullScreen = false;
				_sDescriptor.uFreq		 = 0;
			}
			else
			{
				_sDescriptor.bFullScreen = true;
				_sDescriptor.uFreq		 = sDesc.uFreq;
			}
		}

		// set window style flags //
		if ( sDesc.bFullScreen )
		{
			wndStyle	|= WS_POPUP;
			v_wnd_pos	 = UXMath::ivec2();
		}
		else
		{
			if ( sDesc.eStyle & e_wnd_style::NO_BORDER )
				wndStyle |= WS_OVERLAPPED;
			else
			if ( sDesc.eStyle & e_wnd_style::ALLOW_SIZING )
				wndStyle |= WS_OVERLAPPEDWINDOW;
			else
				wndStyle |= WS_OVERLAPPED | WS_CAPTION |
							WS_SYSMENU | WS_MINIMIZEBOX;
		}

		if ( sDesc.eStyle & e_wnd_style::TOPMOST )
			wndExtStyle |= WS_EX_TOPMOST;

		if ( sDesc.eStyle & e_wnd_style::TOOL )
			wndExtStyle |= WS_EX_TOOLWINDOW;

		RECT winRect = { 0, 0, v_wnd_size.x, v_wnd_size.y };
		AdjustWindowRectEx( &winRect, wndStyle, 0, wndExtStyle );

		if ( sDesc.eStyle & e_wnd_style::CENTERED )
		{
			UXMath::uvec2	v_scr_res = WinApiPlatform::ScreenResolution();
			v_wnd_pos.x = (v_scr_res.x - (winRect.right - winRect.left)) / 2;
			v_wnd_pos.y = (v_scr_res.y - (winRect.bottom - winRect.top)) / 2;
		}


		_sHandles.hWnd = CreateWindowEx( wndExtStyle,
										 _sHandles.sClassName.cstr(),
										 pCaption.Empty() ? "Caption" : pCaption.cstr(),
										 wndStyle,
										 v_wnd_pos.x,	v_wnd_pos.y,
										 winRect.right - winRect.left,
										 winRect.bottom - winRect.top,
										 null, null,
										 _sHandles.hInstance, null );

		_sDescriptor.eStyle			= sDesc.eStyle;
		_sDescriptor.vPos			= v_wnd_pos;
		_sDescriptor.vSize			= v_wnd_size;
		_sDescriptor.bRawInput		= sDesc.bRawInput;
		_sDescriptor.bCanHideCursor	= sDesc.bCanHideCursor;

		CHECK_ERR( _sHandles.hWnd != null );
		
		_sAppCaption = pCaption;

		CHECK_ERR( WinApiPlatform::SetTimer( _sHandles, _uFPSTimer, 1000 ) );

		if ( _sDescriptor.bRawInput )
		{
			RAWINPUTDEVICE	Rid[2];
			// usage params:
			// 0x02 - mouse, 0x04 - joystick, 0x05 - game pad, 0x06 - keyboard
		
			// mouse
			Rid[0].usUsagePage	= 0x01;
			Rid[0].usUsage		= 0x02;
			Rid[0].dwFlags		= 0;
			Rid[0].hwndTarget	= _sHandles.hWnd;

			// keyboard
			Rid[1].usUsagePage	= 0x01;
			Rid[1].usUsage		= 0x06;
			Rid[1].dwFlags		= 0;	// RIDEV_INPUTSINK | RIDEV_NOHOTKEYS | RIDEV_NOLEGACY | RIDEV_REMOVE;
			Rid[1].hwndTarget	= _sHandles.hWnd;
		
			CHECK_ERR(
				RegisterRawInputDevices( Rid, 2, sizeof(Rid[0]) ) == TRUE,
				"Can't register input devices"
			);
		}

		if ( _sDescriptor.bCanHideCursor )
		{
			winapi::BYTE	a_and_icon_mask[32*32/8],
							a_xor_icon_mask[32*32/8]; 

			MemSet( a_and_icon_mask, 0xFF );
			MemSet( a_xor_icon_mask, 0x00 );

			_pCursor = winapi::CreateCursor( Handles().hInstance, 1, 1, 32, 32, a_and_icon_mask, a_xor_icon_mask );
		}

		Hide();
		return true;
	}

/*
=================================================
	установить параметры окна и рендера
=================================================
*/
	bool CWindow::ChangeParams(const TDescriptor &sDesc, bool bHidden)
	{
		DWORD	dw_style	= !bHidden ? WS_VISIBLE : 0;
		DWORD	dw_style_ex	= WS_EX_APPWINDOW;
		TDescriptor		s_new_desc	= sDesc;
		
		ASSERT( All( sDesc.vSize <= WinApiPlatform::ScreenResolution() ) );
		
		// if fullscreen mode //
		if ( _sDescriptor.bFullScreen and !s_new_desc.bFullScreen )
			CHECK_ERR( ChangeDisplaySettings( (DEVMODEA *)null, CDS_RESET ) == DISP_CHANGE_SUCCESSFUL );

		if ( s_new_desc.bFullScreen and
			 !(	_sDescriptor.bFullScreen and
				s_new_desc.uFreq == _sDescriptor.uFreq and
				All( s_new_desc.vSize == _sDescriptor.vSize ) ) )
		{
			CHECK_ERR( WinApiPlatform::ChangeScreenResolution( s_new_desc.vSize, 8, s_new_desc.uFreq ) );
		}

		if ( s_new_desc.bFullScreen )
			dw_style |= WS_POPUP;
		else
		{
			if ( s_new_desc.eStyle & e_wnd_style::NO_BORDER )
				dw_style |= WS_OVERLAPPED;
			else
			if ( s_new_desc.eStyle & e_wnd_style::ALLOW_SIZING )
				dw_style |= WS_OVERLAPPEDWINDOW;
			else
				dw_style |= WS_OVERLAPPED | WS_CAPTION |
							WS_SYSMENU | WS_MINIMIZEBOX;
		}


		if ( s_new_desc.eStyle & e_wnd_style::TOPMOST)
			dw_style_ex |= WS_EX_TOPMOST;

		if ( s_new_desc.eStyle & e_wnd_style::TOOL )
			dw_style_ex |= WS_EX_TOOLWINDOW;


		CHECK_ERR( SetWindowLongA( _sHandles.hWnd, GWL_EXSTYLE, dw_style_ex ) != 0 );
		CHECK_ERR( SetWindowLongA( _sHandles.hWnd, GWL_STYLE, dw_style ) != 0 );

		RECT	winRect = { 0, 0, s_new_desc.vSize.x, s_new_desc.vSize.y };
		AdjustWindowRectEx( &winRect, dw_style, FALSE, dw_style_ex );

		if ( !s_new_desc.bFullScreen ) {
			if ( s_new_desc.eStyle & e_wnd_style::CENTERED ) {
				UXMath::uvec2	v_scr_res = WinApiPlatform::ScreenResolution();
				s_new_desc.vPos.x = ( v_scr_res.x - (winRect.right  - winRect.left) ) / 2;
				s_new_desc.vPos.y = ( v_scr_res.y - (winRect.bottom - winRect.top) ) / 2;
			}
		}
		else
			s_new_desc.vPos = UXMath::ivec2();

		SetWindowPos( _sHandles.hWnd, HWND_TOP, s_new_desc.vPos.x, s_new_desc.vPos.y,
								winRect.right - winRect.left, winRect.bottom - winRect.top, SWP_FRAMECHANGED );
		
		if ( !bHidden )
			Show();
		
		_sDescriptor = s_new_desc;
		return true;
	}
	
/*
=================================================
	
=================================================
*/
	void CWindow::Destroy()
	{
		if ( _pCursor != null )
		{
			OS_CALL( bool os_err = winapi::DestroyCursor( (winapi::HCURSOR)_pCursor ) == winapi::TRUE );
		}

		if ( _sHandles.hWnd != null )
			CHECK_ERR( DestroyWindow( _sHandles.hWnd ) == TRUE, void() );

		_aUpdaters.Clear();

		_bIsLooping			= false;
		_bAllowPause		= false;

		_sHandles.hWnd		= null;
		_sHandles.hInstance	= null;

		_sDescriptor		= TDescriptor();
		
		_sAppCaption.Clear();
		_aEvLists.Clear();

		_pKeyboardInput		= null;
		_uFPSCount			= 0;
		_uLastFPS			= 0;
		_bFPSInCaption		= false;
		
#	ifdef UX_OPENGL_WINDOW_HANDLES
		CHECK( _sHandles.hDC == null )
#	endif
	}

/*
=================================================
	
=================================================
*/
	int CWindow::Loop()
	{
		MSG		s_msg = {0};
		_bIsLooping = true;
		
		SetWindowLongPtrA( _sHandles.hWnd, GWLP_USERDATA, (LONG_PTR) this );
		SetWindowLongPtrA( _sHandles.hWnd, GWLP_WNDPROC,  (LONG_PTR) &_s_WinProc );
		Show();

		while ( _bIsLooping )
		{
			while ( PeekMessageA( &s_msg, null, 0, 0, PM_REMOVE ) )
			{
				if ( WM_QUIT == s_msg.message )
					_bIsLooping = false;
				else
				{   
					DispatchMessageA(  &s_msg );
				}
			}

			_Update();
		}
		
		SetWindowLongPtrA( _sHandles.hWnd, GWLP_WNDPROC, (LONG_PTR)DefWindowProcA );

		CHECK_ERR( ((int)s_msg.wParam) != -1 );
		return true;
	}
	
/*
=================================================
	функция обработки сообщений
=================================================
*/
	LRESULT CALLBACK CWindow::_s_WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		CWindow	*p_wnd = (CWindow *)GetWindowLongPtrA( hWnd, GWLP_USERDATA );
		return p_wnd->_MsgProc( TSystemMsg(uMsg, wParam, lParam) );
	}
	
/*
=================================================
	для запуска в отдельном потоке
=================================================
*/
	void CWindow::_s_ThreadProc(void *pParam)
	{
		TCreateInThreadData *	p_data = (TCreateInThreadData *)pParam;
		CWindow				*	p_wnd  = p_data->pThis;

		p_wnd->_Create( p_data->sDesc, p_data->sCaption.cstr() );
		delete p_data;

		p_wnd->Loop();
	}
	
/*
=================================================
	
=================================================
*/
	void CWindow::AddUpdater(IWindowUpdater *pUpd)
	{
		if ( pUpd != null )
		{
			RemoveUpdater( pUpd );
			_aUpdaters.PushBack( pUpd );
		}
	}

/*
=================================================
	
=================================================
*/
	void CWindow::RemoveUpdater(IWindowUpdater *pUpd)
	{
		FOR( i, _aUpdaters ) {
			if ( pUpd == _aUpdaters[i] ) {
				_aUpdaters.Erase( i );
				return;
			}
		}
	}

/*
=================================================
	завершает цикл обработки сообщений
=================================================
*/
	void CWindow::Quit()
	{
		if ( _bIsLooping )
		{
			_uPause |= ON_DESTROY;
			PostMessageA( _sHandles.hWnd, WM_CLOSE, 0, 0 );
		}
	}

/*
=================================================
	
=================================================
*/
	usize CWindow::_MsgProc (const TSystemMsg &sMsg)
	{
		TWindowEvent	s_event;

		// WM_PAINT //
		if ( sMsg.uMsg == WM_PAINT )
			_Update();
		else

		// WM_INPUT //
		if ( sMsg.uMsg == WM_INPUT and _uPause == 0 )
		{
			uint8	a_data[60];
			uint	u_size = sizeof(a_data);

			if ( GetRawInputData( (HRAWINPUT)sMsg.lParam, RID_INPUT, a_data, &u_size, sizeof(RAWINPUTHEADER) ) != -1 )
			{
				RAWINPUT  *	p_data = (RAWINPUT *)a_data;

				// KEYBOARD //
				if ( p_data->header.dwType == RIM_TYPEKEYBOARD )
				{
					uint16		u_right = p_data->data.keyboard.Flags & RI_KEY_E0 ? e_win_key::right_offset : 0;
					
					if ( p_data->data.keyboard.VKey + u_right >= e_win_key::_COUNT )
						u_right = 0;

					s_event.eEventType				= e_wnd_event::KEY;
					s_event.sEvent.sKey.bPressed	= EnumNoCmp( p_data->data.keyboard.Flags, RI_KEY_BREAK );
					s_event.sEvent.sKey.eKey		= e_win_key::type( p_data->data.keyboard.VKey + u_right );
					
					if ( s_event.sEvent.sKey.bPressed )
					{
						BYTE	a_all_keys[256];
						GetKeyboardState( a_all_keys );

						WORD	a_key_chars[2] = {0};
						UINT	u_scan_code	= MapVirtualKeyA( p_data->data.keyboard.VKey, MAPVK_VK_TO_VSC );
						int				res = ToAsciiEx( p_data->data.keyboard.VKey, u_scan_code, a_all_keys,
																a_key_chars, 0, _pKeyboardInput );

						s_event.sEvent.sKey.cChar = ( res >= 1 ? (char)a_key_chars[0] : 0 );

						res = ToUnicodeEx( p_data->data.keyboard.VKey, u_scan_code, a_all_keys,
													(LPWSTR)a_key_chars, 2, 0, _pKeyboardInput );
					
						s_event.sEvent.sKey.wChar = ( res >= 1 ? (wchar)a_key_chars[0] : 0 );
					}
					else
					{
						s_event.sEvent.sKey.cChar = 0;
						s_event.sEvent.sKey.wChar = 0;
					}

					SendEvent( s_event );
				}
				else
					
				// MOUSE //
				if ( p_data->header.dwType == RIM_TYPEMOUSE )
				{
					s_event.eEventType			 = e_wnd_event::MOUSE;
					s_event.sEvent.sMouse.iX	 = p_data->data.mouse.lLastX;
					s_event.sEvent.sMouse.iY	 = p_data->data.mouse.lLastY;
					s_event.sEvent.sMouse.bDelta = true;
					SendEvent( s_event );


					s_event.eEventType							= e_wnd_event::MOUSE_KEY;
					
					s_event.sEvent.sMouseKey.bButton1Pressed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_1_DOWN );
					s_event.sEvent.sMouseKey.bButton2Pressed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_2_DOWN );
					s_event.sEvent.sMouseKey.bButton3Pressed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_3_DOWN );
					s_event.sEvent.sMouseKey.bButton4Pressed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_4_DOWN );
					s_event.sEvent.sMouseKey.bButton5Pressed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_5_DOWN );

					s_event.sEvent.sMouseKey.bButton1Changed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_1_DOWN | RI_MOUSE_BUTTON_1_UP );
					s_event.sEvent.sMouseKey.bButton2Changed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_2_DOWN | RI_MOUSE_BUTTON_2_UP );
					s_event.sEvent.sMouseKey.bButton3Changed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_3_DOWN | RI_MOUSE_BUTTON_3_UP );
					s_event.sEvent.sMouseKey.bButton4Changed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_4_DOWN | RI_MOUSE_BUTTON_4_UP );
					s_event.sEvent.sMouseKey.bButton5Changed	= EnumCmp( p_data->data.mouse.usButtonFlags, RI_MOUSE_BUTTON_5_DOWN | RI_MOUSE_BUTTON_5_UP );
					
					if ( p_data->data.mouse.usButtonFlags & RI_MOUSE_WHEEL )
						s_event.sEvent.sMouseKey.iDeltaWheel = int16( p_data->data.mouse.usButtonData ) / WHEEL_DELTA;
					else
						s_event.sEvent.sMouseKey.iDeltaWheel = 0;

					SendEvent( s_event );
				}
			}
		}
		else

		// WM_MOUSEMOVE //
		if ( sMsg.uMsg == WM_MOUSEMOVE and _uPause == 0 )
		{
			s_event.eEventType			 = e_wnd_event::MOUSE;
			s_event.sEvent.sMouse.iX	 = LOWORD( sMsg.lParam );
			s_event.sEvent.sMouse.iY	 = _sDescriptor.vSize.y - HIWORD( sMsg.lParam );
			s_event.sEvent.sMouse.bDelta = false;
			SendEvent( s_event );
		}
		else

		// WM_TIMER //
		if ( sMsg.uMsg == WM_TIMER and sMsg.wParam == _uFPSTimer )
		{
			_uLastFPS  = _uFPSCount;
			_uFPSCount = 0;

			if ( _bFPSInCaption and _uPause == 0 )
				SetText( (string(_sAppCaption) << " [FPS: " << string().FormatI(_uLastFPS,10) << "]").cstr() );
		}
		else

		// WM_KILLPOCUS //
		if ( sMsg.uMsg == WM_KILLFOCUS ) {
			_uPause |= NO_FOCUS;
			s_event.eEventType			= e_wnd_event::MESSAGE;
			s_event.sEvent.sMsg.eMsg	= TWindowEvent::TMessage::PAUSE;
			SendEvent( s_event );
		}
		else

		// WM_SETFOCUS //
		if ( sMsg.uMsg == WM_SETFOCUS ) {
			_uPause &= ~NO_FOCUS;
			s_event.eEventType			= e_wnd_event::MESSAGE;
			s_event.sEvent.sMsg.eMsg	= TWindowEvent::TMessage::RESUME;
			SendEvent( s_event );
		}
		else

		// WM_SIZE //
		if ( sMsg.uMsg == WM_SIZE )
		{
			s_event.eEventType = e_wnd_event::MESSAGE;

			if ( sMsg.wParam == SIZE_MINIMIZED )
				s_event.sEvent.sMsg.eMsg = TWindowEvent::TMessage::MINIMIZED;
			else
			{
				_sDescriptor.vSize.x = LOWORD(sMsg.lParam);
				_sDescriptor.vSize.y = HIWORD(sMsg.lParam);

				if ( sMsg.wParam == SIZE_MAXIMIZED )
					s_event.sEvent.sMsg.eMsg = TWindowEvent::TMessage::MAXIMIZED;
				else
					s_event.sEvent.sMsg.eMsg = TWindowEvent::TMessage::RESIZE;
			}
			SendEvent( s_event );
		}
		else

		// WM_MOVE //
		if ( sMsg.uMsg == WM_MOVE )
		{
			_sDescriptor.vPos = GetWindowPos();

			s_event.eEventType			= e_wnd_event::MESSAGE;
			s_event.sEvent.sMsg.eMsg	= TWindowEvent::TMessage::MOVE;
			SendEvent( s_event );
		}
		else

		// WM_USER //
		if ( sMsg.uMsg == WM_USER )
		{
			s_event.eEventType				= e_wnd_event::USER;
			s_event.sEvent.sUser.iParam1	= sMsg.wParam;
			s_event.sEvent.sUser.iParam2	= sMsg.lParam;
			SendEvent( s_event );
		}
		else

		// WM_SYSCOMMAND //
		if ( sMsg.uMsg == WM_SYSCOMMAND and (sMsg.wParam == SC_SCREENSAVE || sMsg.wParam == SC_MONITORPOWER) )
			return 0;
		else

		// WM_CLOSE //
		if ( sMsg.uMsg == WM_CLOSE ) {
			PostMessageA( _sHandles.hWnd, WM_QUIT, 0, 0 );
			return 1;
		}
		else
			
		// WM_INPUTLANGCHANGE //
		if ( sMsg.uMsg == WM_INPUTLANGCHANGE ) {
			_pKeyboardInput	= (HKL) LOWORD( GetKeyboardLayout(0) );
			return 0; 
		}
		else

		// WM_DESTROY //
		if ( sMsg.uMsg == WM_DESTROY ) {
			CHECK_ERR( WinApiPlatform::KillTimer( _sHandles, _uFPSTimer ) );
		}

		return DefWindowProcA( _sHandles.hWnd, sMsg.uMsg, sMsg.wParam, sMsg.lParam );
	}

/*
=================================================
	
=================================================
*/
	void CWindow::AddEventListener (IWindowEventListener *pEvList, e_wnd_event::type eEvents)
	{
		if ( pEvList != null and eEvents != e_wnd_event::UNKNOWN )
		{
			RemoveEventListener( pEvList );
			_aEvLists.PushBack( TListener( pEvList, eEvents ) );
		}
	}

/*
=================================================
	
=================================================
*/
	void CWindow::RemoveEventListener (IWindowEventListener *pEvList)
	{
		if ( pEvList == null )
			return;

		FOR( i, _aEvLists ) {
			if ( _aEvLists[i].pListener == pEvList ) {
				_aEvLists.Erase( i );
				return;
			}
		}
	}
	
/*
=================================================
	вызвать обновление всех объектов
=================================================
*/
	void CWindow::_Update ()
	{
		if ( (_uPause == 0) | !_bAllowPause )
		{
			++_uFPSCount;

			FOR( i, _aUpdaters ) {
				_aUpdaters[i]->OnUpdate( this );
			}
		}
		else
			WinApiPlatform::Yield();
	}
		
/*
=================================================
	отправить сообщение всем слушателям
=================================================
*/
	void CWindow::SendEvent (const TWindowEvent &sEvent)
	{
		FOR( i, _aEvLists ) {
			if ( EnumCmp( _aEvLists[i].eEvents, sEvent.eEventType ) )
				_aEvLists[i].pListener->ProcessMessage( sEvent );
		}
	}

//-------------------------------------------------------------------

}	// _platform_
}	// UX_STL

#endif	// PLATFORM_WINDOWS